package com.himaci.gtbattle.controller;

import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
import com.himaci.gtbattle.model.*;
import com.himaci.gtbattle.model.character.Archer;
import com.himaci.gtbattle.model.character.Mage;
import com.himaci.gtbattle.model.character.Priest;
import com.himaci.gtbattle.resource.Assets;
import com.himaci.gtbattle.view.WorldRenderer;

/**
 * Kelas mengontrol gameplay
 *
 * @author XNPSDN
 */
public class GameplayController {

    public static final int STATE_WAIT = 0;
    public static final int STATE_READY = 1;
    public static final int STATE_CHAR_SELECTED = 2;
    public static final int STATE_SKILL_SELECTED = 3;
    public static final int STATE_TARGET_SELECTED = 4;
    public static final int STATE_GAME_FINISHED = 5;
    private static GameplayController instance;
    // world
    private World world;
    private WorldRenderer worldRenderer;
    private SpriteBatch batcher;
    private OrthographicCamera camera;
    // level AI yang digunakan
    public int aiLevel;
    // state
    private int actionState;
    private int playerTurn;
    private int selectedChar;
    private int selectedSkill;
    private int selectedTarget;
    private int winner;
    private BitmapFont fontNormal;
    private BitmapFont fontImage;
    // animation flag
    private final int PRE_SKILL = 0;
    private final int AFTER_SKILL = 1;
    private final int PRE_ADV = 2;
    private final int AFTER_ADV = 3;
    private int animFlag;
    // other    
    private Rectangle skillRec1;
    private Rectangle skillRec2;
    private Rectangle skillRec3;
    private Rectangle okRec;
    private Rectangle cancelRec;
    private Rectangle skipRec;

    /*
     * Private konstruktor
     */
    private GameplayController() {
	fontNormal = Assets.getInstance().fontNormal;
	fontImage = Assets.getInstance().fontImage;

	skillRec1 = new Rectangle(240, 5, 50, 50);
	skillRec2 = new Rectangle(290, 5, 50, 50);
	skillRec3 = new Rectangle(340, 5, 50, 50);

	okRec = new Rectangle(375, 5, 50, 50);
	cancelRec = new Rectangle(425, 5, 50, 50);
	skipRec = new Rectangle(395, 10, 80, 40);
    }

    /**
     * Mendapatkan singleton instance
     * @return Instance dari kelas ActionController
     */
    public static synchronized GameplayController getInstance() {
	if (instance == null) {
	    instance = new GameplayController();
	}
	return instance;
    }

    /**
     * Set the condition with some world
     * @param world World
     * @param worldRenderer World renderer
     */
    public void set(SpriteBatch batcher, OrthographicCamera camera) {
	this.batcher = batcher;
	this.camera = camera;
    }

    /**
     * Reset world
     */
    public void reset() {
	world = new World(AI.NO_AI, aiLevel);
	worldRenderer = new WorldRenderer(world, batcher);

	actionState = STATE_READY;
	playerTurn = Player.PLAYER_ONE;
	selectedChar = 0;
	selectedSkill = 0;
	selectedTarget = 0;

	winner = -1;
	animFlag = PRE_SKILL;
    }

    /**
     * Update game
     * @param deltaTime Update time
     */
    public void update(float deltaTime) {
	// update the world
	world.update(deltaTime);

	// update based on state
	if (actionState == GameplayController.STATE_WAIT) {
	    if (animFlag == PRE_SKILL) {
		// lagi animasi ngeluarin skill
		if (world.isReady()) {
		    //world.display();
		    Logger.getInstance().addLog(
			    Action.getInstance().execute(playerTurn, convertCharId(selectedChar), selectedSkill, convertCharId(selectedTarget), world));
		    animFlag = AFTER_SKILL;

		    // cek buat statistik disini
		    if (playerTurn == Player.PLAYER_ONE 
			    && world.player2.ai.generatedActions.size() > 0
			    && world.player2.ai.currentActionExecuted < world.player2.ai.generatedActions.size()) {
			Action lastAction = Logger.getInstance().gameActionLog.get(Logger.getInstance().gameActionLog.size() - 1);
			Action estimatedAction = world.player2.ai.generatedActions.get(world.player2.ai.currentActionExecuted);
			if (estimatedAction.playerNumber != Player.PLAYER_TWO) {
			    if (lastAction == estimatedAction) {
				Logger.getInstance().predictedMove++;
			    } else {
				Logger.getInstance().unpredictedMove++;
			    }
			}

			// tes
			System.out.println("ACTIONDETECT " + Logger.getInstance().predictedMove + " " + Logger.getInstance().unpredictedMove);
		    }	    
		}
	    } else if (animFlag == AFTER_SKILL) {
		// lagi animasi kena damage
		if (world.isReady()) {
		    // trigger animasi adv here...
		    world.animAdvance();
		    animFlag = PRE_ADV;
		}
	    } else if (animFlag == PRE_ADV) {
		// lagi animasi mukul
		if (world.isReady()) {
		    world.projectileTime += deltaTime;
		}
		// udah sampe projectilenya
		if (world.isReady() && world.projectileTime > World.Projectile.PROJECTILE_TIME) {
		    world.clearProjectile();

		    world.advance();
		    animFlag = AFTER_ADV;
		}
	    } else if (animFlag == AFTER_ADV) {
		// lagi animasi kena damage
		if (world.isReady()) {
		    // reset state
		    playerTurn = playerTurn == Player.PLAYER_ONE ? Player.PLAYER_TWO : Player.PLAYER_ONE;
		    selectedChar = -1;
		    selectedSkill = -1;
		    selectedTarget = -1;

		    actionState = GameplayController.STATE_READY;
		    animFlag = PRE_SKILL;
		}
	    }
	} else if (actionState == GameplayController.STATE_READY) {
	    winner = world.gameOver();
	    if (winner != -1) {
		System.out.println("GAME OVER!!!");
		System.out.println("Pemenangnya adalah : Player " + winner);

		actionState = STATE_GAME_FINISHED;
		return;
	    }

	    if (playerTurn == Player.PLAYER_ONE) {
		// ga pake AI
		if (world.player1.ai.aiLevel != AI.NO_AI) {
		    // kalau udah ada aksi yang digenerate
		    if (world.player1.ai.generatedActions.size() > 0) {
			// kalau belum berlebih
			if (world.player1.ai.currentActionExecuted < world.player1.ai.generatedActions.size() - 1) {
			    Action lastAction = Logger.getInstance().gameActionLog.get(Logger.getInstance().gameActionLog.size() - 1);
			    Action estimatedAction = world.player1.ai.generatedActions.get(world.player1.ai.currentActionExecuted);
			    if (lastAction == estimatedAction) {
				world.player1.ai.currentActionExecuted++;
			    } else {
				world.player1.ai.generateAction(world);
			    }
			} else {
			    world.player1.ai.generateAction(world);
			}
		    } else {
			world.player1.ai.generateAction(world);
		    }

		    Action action = world.player1.ai.generatedActions.get(world.player1.ai.currentActionExecuted);
		    world.player1.ai.currentActionExecuted++;

		    if (action == null) {
			actionState = STATE_WAIT;
			animFlag = AFTER_SKILL;
			Logger.getInstance().addLog(null);
			return;
		    }

		    selectedChar = action.executorNumber + 1;
		    selectedSkill = action.actionNumber;
		    selectedTarget = action.targetNumber + 1;

		    // trigger animasi skill
		    if (playerTurn == Player.PLAYER_ONE) {
			world.player1.characters[convertCharId(selectedChar)].animSkill();
		    } else {
			world.player2.characters[convertCharId(selectedChar)].animSkill();
		    }
		    actionState = STATE_WAIT;
		}
	    } else {
		// ga pake AI
		if (world.player2.ai.aiLevel != AI.NO_AI) {
		    // kalau udah ada aksi yang digenerate
		    if (world.player2.ai.generatedActions.size() > 0) {
			// kalau belum berlebih
			if (world.player2.ai.currentActionExecuted < world.player2.ai.generatedActions.size() - 1) {
			    Action lastAction = Logger.getInstance().gameActionLog.get(Logger.getInstance().gameActionLog.size() - 1);
			    Action estimatedAction = world.player2.ai.generatedActions.get(world.player2.ai.currentActionExecuted);
			    if (lastAction == estimatedAction) {
				world.player2.ai.currentActionExecuted++;
			    } else {
				world.player2.ai.generateAction(world);
			    }
			} else {
			    world.player2.ai.generateAction(world);
			}
		    } else {
			world.player2.ai.generateAction(world);
		    }

		    Action action = world.player2.ai.generatedActions.get(world.player2.ai.currentActionExecuted);
		    world.player2.ai.currentActionExecuted++;

		    if (action == null) {
			actionState = STATE_WAIT;
			animFlag = AFTER_SKILL;
			Logger.getInstance().addLog(null);
			return;
		    }

		    selectedChar = action.executorNumber + 1;
		    selectedSkill = action.actionNumber;
		    selectedTarget = action.targetNumber + 1;

		    // trigger animasi skill
		    if (playerTurn == Player.PLAYER_ONE) {
			world.player1.characters[convertCharId(selectedChar)].animSkill();
		    } else {
			world.player2.characters[convertCharId(selectedChar)].animSkill();
		    }
		    actionState = STATE_WAIT;
		}
	    }
	} else if (actionState == GameplayController.STATE_CHAR_SELECTED) {
	    // nothing
	} else if (actionState == GameplayController.STATE_SKILL_SELECTED) {
	    // nothing
	} else if (actionState == GameplayController.STATE_TARGET_SELECTED) {
	    // nothing
	} else if (actionState == GameplayController.STATE_GAME_FINISHED) {
	    // nothing
	}
    }

    /**
     * Render game, SpriteBatch sudah mulai sebelumnya
     */
    public void render() {
	// render the background
	batcher.draw(Assets.getInstance().gameBg, 0, 0);

	// render the world
	worldRenderer.render();

	// render based on state
	if (actionState == GameplayController.STATE_WAIT) {
	    // nothing
	    if (animFlag == PRE_SKILL) {
		Skill tempSkill = getSkill();
		if (tempSkill != null) {
		    fontImage.setScale(1.0f);
		    fontImage.draw(batcher, tempSkill.skillName.toUpperCase(),
			    240 - (fontImage.getBounds(tempSkill.skillName.toUpperCase()).width / 2), 290);
		}
	    }
	} else if (actionState == GameplayController.STATE_READY) {
	    // render skip button
	    if (playerTurn == Player.PLAYER_ONE) {
		fontNormal.setColor(Color.WHITE);
		fontNormal.setScale(1.0f);
		fontNormal.draw(batcher, "Pilih Karakter!", 10, 60);
		batcher.draw(Assets.getInstance().skipButton, skipRec.x, skipRec.y, skipRec.width, skipRec.height);
	    }
	} else if (actionState == GameplayController.STATE_CHAR_SELECTED) {
	    // gambar pointer diatas character
	    // gambar character info & skill button
	    worldRenderer.drawCharPointer(selectedChar);

	    // reset kamera
	    camera.update();
	    batcher.setProjectionMatrix(camera.combined);

	    // render lagi
	    renderCharInfo();
	    // render skip button
	    batcher.draw(Assets.getInstance().skipButton, skipRec.x, skipRec.y, skipRec.width, skipRec.height);
	} else if (actionState == GameplayController.STATE_SKILL_SELECTED) {
	    // gambar info skill
	    worldRenderer.drawCharPointer(selectedChar);

	    // reset kamera
	    camera.update();
	    batcher.setProjectionMatrix(camera.combined);

	    // render lagi
	    renderSkillInfo();
	    renderConfirmationButton();
	} else if (actionState == GameplayController.STATE_TARGET_SELECTED) {
	    // gambar pointer diatas target
	    worldRenderer.drawCharPointer(selectedChar);
	    worldRenderer.drawTargetPointer(selectedTarget);

	    // reset kamera
	    camera.update();
	    batcher.setProjectionMatrix(camera.combined);

	    // render lagi
	    renderSkillInfo();
	    renderConfirmationButton();
	} else if (actionState == GameplayController.STATE_GAME_FINISHED) {
	    // render win win solution
	}
    }

    /**
     * Todo while screen touched
     * @param touchPoint Touch point relative to camera
     */
    public void touch(int x, int y) {
	Vector3 relativeTouchPoint = new Vector3();
	camera.unproject(relativeTouchPoint.set(x, y, 0));

	if (actionState == GameplayController.STATE_WAIT) {
	    // nothing to click
	} else if (actionState == GameplayController.STATE_READY) {
	    // skip pake skill
	    if (skipRec.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		actionState = STATE_WAIT;
		animFlag = AFTER_SKILL;
		Logger.getInstance().addLog(null);
		return;
	    }
	    // pilih character, passing ke renderer
	    selectedChar = worldRenderer.touchChar(x, y);
	    if (selectedChar != -1) {
		actionState = STATE_CHAR_SELECTED;
	    }
	} else if (actionState == GameplayController.STATE_CHAR_SELECTED) {
	    // skip pake skill
	    if (skipRec.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		actionState = STATE_WAIT;
		animFlag = AFTER_SKILL;
		Logger.getInstance().addLog(null);
		return;
	    }
	    if (getPlayer() == getChar().owner) {
		// pilih skill
		if (skillRec1.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		    // kalau memungkinkan skill 0 digunakan di klik
		    if (getChar().canUseSkill(0)) {
			selectedSkill = 0;
			actionState = STATE_SKILL_SELECTED;
		    }
		} else if (skillRec2.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		    // kalau memungkinkan skill 1 digunakan di klik
		    if (getChar().canUseSkill(1)) {
			selectedSkill = 1;
			actionState = STATE_SKILL_SELECTED;
		    }
		} else if (skillRec3.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		    // kalau memungkinkan skill 0 digunakan di klik
		    if (getChar().canUseSkill(2)) {
			selectedSkill = 2;
			actionState = STATE_SKILL_SELECTED;
		    }
		} else {
		    // pilih character, passing ke renderer
		    selectedChar = worldRenderer.touchChar(x, y);
		    if (selectedChar != -1) {
			actionState = STATE_CHAR_SELECTED;
		    } else {
			actionState = STATE_READY;
		    }
		}
	    } else {
		// pilih character, passing ke renderer
		selectedChar = worldRenderer.touchChar(x, y);
		if (selectedChar != -1) {
		    actionState = STATE_CHAR_SELECTED;
		} else {
		    actionState = STATE_READY;
		}
	    }
	} else if (actionState == GameplayController.STATE_SKILL_SELECTED) {
	    // cancel
	    if (cancelRec.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		cancel();
	    } else {
		// pilih character, passing ke renderer
		selectedTarget = worldRenderer.touchChar(x, y);
		if (selectedTarget != -1) {
		    actionState = STATE_TARGET_SELECTED;
		}
	    }
	} else if (actionState == GameplayController.STATE_TARGET_SELECTED) {
	    // konfirmasi
	    if (okRec.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		ok();
	    } else if (cancelRec.contains(relativeTouchPoint.x, relativeTouchPoint.y)) {
		cancel();
	    } else {
		// pilih character, passing ke renderer
		selectedTarget = worldRenderer.touchChar(x, y);
	    }
	} else if (actionState == GameplayController.STATE_GAME_FINISHED) {
	    // touch pindah ke screen lain
	}

    }

    /**
     * Execute
     */
    public void ok() {
	if (!canExecuteSkill()) {
	    return;
	}

	// trigger animasi skill
	if (playerTurn == Player.PLAYER_ONE) {
	    world.player1.characters[convertCharId(selectedChar)].animSkill();
	} else {
	    world.player2.characters[convertCharId(selectedChar)].animSkill();
	}
	actionState = STATE_WAIT;
    }

    /**
     * Cancel targetting
     */
    public void cancel() {
	actionState = STATE_CHAR_SELECTED;
	selectedTarget = -1;
	selectedSkill = -1;
    }

    /**
     * Mengambil giliran player yang aktif
     * @return Player
     */
    public Player getPlayer() {
	if (playerTurn == Player.PLAYER_ONE) {
	    return world.player1;
	} else {
	    return world.player2;
	}
    }

    /**
     * Mengambil character yang dipilih
     * @return Karakter
     */
    public GameChar getChar() {
	if (selectedChar >= 1 && selectedChar <= 3) {
	    return world.player1.characters[convertCharId(selectedChar)];
	} else if (selectedChar >= 4 && selectedChar <= 6) {
	    return world.player2.characters[convertCharId(selectedChar)];
	}
	return null;
    }

    /**
     * Mengambil skill yang dipilih
     * @return Skill
     */
    public Skill getSkill() {
	return Skill.getSkill(convertCharId(selectedChar), selectedSkill);
    }

    /**
     * Mengambil target yang dipilih
     * @return Karakter
     */
    public GameChar getTarget() {
	if (selectedTarget >= 1 && selectedTarget <= 3) {
	    return world.player1.characters[convertCharId(selectedTarget)];
	} else if (selectedTarget >= 4 && selectedTarget <= 6) {
	    return world.player2.characters[convertCharId(selectedTarget)];
	}
	return null;
    }

    /**
     * Mengkonvert char id ke dalam action
     * @param rawNumber Nomor lama
     * @return Nomor baru
     */
    public int convertCharId(int rawNumber) {
	if (rawNumber >= 1 && rawNumber <= 3) {
	    return rawNumber - 1;
	} else if (rawNumber >= 4 && rawNumber <= 6) {
	    return rawNumber - 4;
	}
	return -1;
    }

    /**
     * Render char info and skill
     */
    private void renderCharInfo() {
	GameChar tempChar = getChar();

	// draw the dock
	batcher.draw(Assets.getInstance().dockBox, 0, 0, 480, 60);

	// draw info
	fontNormal.setColor(Color.BLACK);
	fontNormal.setScale(0.5f);
	if (tempChar instanceof Archer) {
	    batcher.draw(Assets.getInstance().archer, 5, 5, 50, 50);
	    fontNormal.draw(batcher, "Archer", 60, 60);
	} else if (tempChar instanceof Mage) {
	    batcher.draw(Assets.getInstance().mage, 5, 5, 50, 50);
	    fontNormal.draw(batcher, "Mage", 60, 60);
	} else if (tempChar instanceof Priest) {
	    batcher.draw(Assets.getInstance().priest, 5, 5, 50, 50);
	    fontNormal.draw(batcher, "Priest", 60, 60);
	}
	fontNormal.setScale(0.4f);
	fontNormal.draw(batcher, "HP : " + tempChar.currVitality + "/" + tempChar.maxVitality, 60, 40);
	fontNormal.draw(batcher, "MP : " + tempChar.currEnergy + "/" + tempChar.maxEnergy, 60, 25);
	fontNormal.draw(batcher, "ATK : " + tempChar.getAttack(), 150, 40);
	fontNormal.draw(batcher, "DEF : " + tempChar.currDefense, 150, 25);

	// draw the skill
	if (getPlayer() == tempChar.owner) {
	    // gambar char info dan skill
	    batcher.draw(Assets.getInstance().skillButton[convertCharId(selectedChar)][0], skillRec1.x, skillRec1.y, skillRec1.width, skillRec1.height);
	    batcher.draw(Assets.getInstance().skillButton[convertCharId(selectedChar)][1], skillRec2.x, skillRec2.y, skillRec2.width, skillRec2.height);
	    batcher.draw(Assets.getInstance().skillButton[convertCharId(selectedChar)][2], skillRec3.x, skillRec3.y, skillRec3.width, skillRec3.height);
	    // gambar disable kalo ga bisa di klik
	    if (!tempChar.canUseSkill(0)) {
		batcher.draw(Assets.getInstance().disableIcon, skillRec1.x, skillRec1.y, skillRec1.width, skillRec1.height);
	    }
	    if (!tempChar.canUseSkill(1)) {
		batcher.draw(Assets.getInstance().disableIcon, skillRec2.x, skillRec2.y, skillRec2.width, skillRec2.height);
	    }
	    if (!tempChar.canUseSkill(2)) {
		batcher.draw(Assets.getInstance().disableIcon, skillRec3.x, skillRec3.y, skillRec3.width, skillRec3.height);
	    }
	}
    }

    /**
     * Render skill info detail
     */
    private void renderSkillInfo() {
	Skill tempSkill = getSkill();
	GameChar tempTarget = getTarget();

	// draw the dock
	batcher.draw(Assets.getInstance().dockBox, 0, 0, 480, 60);

	// draw the skill detail
	batcher.draw(Assets.getInstance().skillButton[convertCharId(selectedChar)][selectedSkill], 5, 5, 50, 50);
	fontNormal.setColor(Color.BLACK);
	fontNormal.setScale(0.5f);
	fontNormal.draw(batcher, tempSkill.skillName + "(" + tempSkill.manaCost + ")", 60, 60);
	fontNormal.setScale(0.4f);
	fontNormal.drawWrapped(batcher, tempSkill.skillDesc, 60, 40, 170);

	// draw target
	if (tempTarget == null) {
	    fontNormal.draw(batcher, "Pilih Target!", 250, 60);
	} else {
	    // gambar dan nama
	    if (tempTarget instanceof Archer) {
		fontNormal.draw(batcher, "Target : Archer", 250, 60);
		batcher.draw(Assets.getInstance().archer, 250, 5, 35, 35);
	    } else if (tempTarget instanceof Mage) {
		fontNormal.draw(batcher, "Target : Mage", 250, 60);
		batcher.draw(Assets.getInstance().mage, 250, 5, 35, 35);
	    } else if (tempTarget instanceof Priest) {
		fontNormal.draw(batcher, "Target : Priest", 250, 60);
		batcher.draw(Assets.getInstance().priest, 250, 5, 35, 35);
	    }

	    // atribut
	    fontNormal.draw(batcher, "HP : " + tempTarget.currVitality + "/" + tempTarget.maxVitality, 290, 45);
	    fontNormal.draw(batcher, "MP : " + tempTarget.currEnergy + "/" + tempTarget.maxEnergy, 290, 30);
	}
    }

    /**
     * Render button ok dan cancel
     */
    private void renderConfirmationButton() {
	// gambar button
	if (actionState == STATE_TARGET_SELECTED) {
	    batcher.draw(Assets.getInstance().okButton, okRec.x, okRec.y, okRec.width, okRec.height);
	    // kalo ga bisa execute, disable gambarnya	   
	    if (!canExecuteSkill()) {
		batcher.draw(Assets.getInstance().disableIcon, okRec.x, okRec.y, okRec.width, okRec.height);
	    }
	}
	batcher.draw(Assets.getInstance().cancelButton, cancelRec.x, cancelRec.y, cancelRec.width, cancelRec.height);
    }

    // kotor biarin, yang penting mint
    private boolean canExecuteSkill() {
	// executor dan target beda player
	if (((selectedChar >= 1 && selectedChar <= 3) && (selectedTarget >= 4 && selectedTarget <= 6))
		|| ((selectedChar >= 4 && selectedChar <= 6) && (selectedTarget >= 1 && selectedTarget <= 3))) {
	    if (selectedChar == 3 || selectedChar == 6) {
		return false;
	    }
	} else {
	    if (selectedChar != 3 && selectedChar != 6) {
		return false;
	    }
	}
	return Action.getInstance().canExecute(playerTurn, convertCharId(selectedChar), selectedSkill, convertCharId(selectedTarget), world);
    }

    /**
     * Game over atau tidak
     * @return Kalau selain -1, berarti ga game over
     */
    public int isGameOver() {
	return winner;
    }
}
